home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / modules / FlockXPCOMUtils.jsm < prev    next >
Text File  |  2007-10-18  |  16KB  |  381 lines

  1. //
  2. // BEGIN FLOCK GPL
  3. //
  4. // Copyright Flock Inc. 2005-2007
  5. // http://flock.com
  6. //
  7. // This file may be used under the terms of of the
  8. // GNU General Public License Version 2 or later (the "GPL"),
  9. // http://www.gnu.org/licenses/gpl.html
  10. //
  11. // Software distributed under the License is distributed on an "AS IS" basis,
  12. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. // for the specific language governing rights and limitations under the
  14. // License.
  15. //
  16. // END FLOCK GPL
  17. //
  18. // This code is based on the Javascript part of the proposed patch
  19. // to Mozilla found here:
  20. //
  21. //    https://bugzilla.mozilla.org/show_bug.cgi?id=71689
  22. //
  23. // Bugzilla Bug 71689 - Need helper utilities for JS component authors
  24. //  "They suffer without a generic module implementation, and the
  25. //   cutting-and-pasting breaks my little heart."  (a-yup)
  26. //
  27. // All objects in here become properties on the 'FlockXPCOMUtils' object.
  28.  
  29. const CC = Components.classes;
  30. const CI = Components.interfaces;
  31. const CR = Components.results;
  32.  
  33. var EXPORTED_SYMBOLS = [ "FlockXPCOMUtils" ];
  34.  
  35. debug("*** loading FlockXPCOMUtils\n");
  36.  
  37. var FlockXPCOMUtils = {
  38.  
  39.   debug: false,
  40.  
  41. /**
  42.  * Creates an NSGetModule function that returns the result of calling
  43.  * genericModule with the same parameters.
  44.  */
  45.   generateNSGetModule: function FlockXPCOMUtils_generateNSGetModule(aName, aComponentsArray) {
  46.     return function FlockXPCOMUtils_NSGetModule(aCompMgr, aFileSpec) {
  47.       return new FlockXPCOMUtils.genericModule(aName, aComponentsArray);
  48.     };
  49.   },
  50.  
  51. /**
  52.  * Creates a generic Javascript XPCOM module to contain one or more XPCOM
  53.  * components also written in Javascript.
  54.  *
  55.  * @param aName (in) A descriptive name for the module. Only used for
  56.  *                   identifying purposes in debug messages.
  57.  * @param aComponentsArray (in) 
  58.  *        
  59.  * @return An object that implements the methods required to participate in
  60.  *         XPCOM: registerSelf, unregisterSelf, getClassObject, canUnload.
  61.  */
  62.     genericModule: function FlockXPCOMUtils_genericModule(aName, aComponentsArray) {
  63.  
  64.     // the first argument is the module name (used for debugging only)
  65.     this.moduleName = aName;
  66.  
  67.     // and the second is an array of components
  68.     this.classes = [];
  69.     for each (let component in aComponentsArray) {
  70.       this.classes.push(FlockXPCOMUtils.classDescForComponent(component));
  71.     }
  72.  
  73.     // use FlockXPCOMUtils as the default for printing debugging statements
  74.     this.debug = FlockXPCOMUtils.debug;
  75.  
  76.     // override printDebug() to make messages slightly more informative
  77.     this.printDebug = function genericModule_printDebug(message) {
  78.       if (this.debug) {
  79.         FlockXPCOMUtils.printDebug("FlockXPCOMUtils Mod: " + this.moduleName + ": " + message);
  80.       }
  81.     };
  82.  
  83.     if (this.classes.length === 0) {
  84.       this.printDebug("(WARNING) no components given to genericModule");
  85.     }
  86.  
  87.     this.registerSelf = function genericModule_registerSelf(aCompMgr, aFileSpec, aLocation, aType) {
  88.       this.printDebug("entering registerSelf...");
  89.       var registrar = aCompMgr.QueryInterface(CI.nsIComponentRegistrar);
  90.  
  91.       for each (let classDesc in this.classes) {
  92.         this.printDebug(" registering component '" + classDesc.className + "'");
  93.         registrar.registerFactoryLocation(classDesc.classID,
  94.                                           classDesc.className,
  95.                                           classDesc.contractID,
  96.                                           aFileSpec,
  97.                                           aLocation,
  98.                                           aType);
  99.         FlockXPCOMUtils.addCategories(classDesc);
  100.         // callback to allow component specific registration
  101.         classDesc.register(aCompMgr, aFileSpec, aLocation, aType);
  102.       }
  103.       this.printDebug("leaving registerSelf");
  104.     };
  105.  
  106.     this.unregisterSelf = function genericModule_unregisterSelf(aCompMgr, aFileSpec, aLocation) {
  107.       this.printDebug("entering unregisterSelf...");
  108.       var registrar = aCompMgr.QueryInterface(CI.nsIComponentRegistrar);
  109.  
  110.       for each (let classDesc in this.classes) {
  111.         this.printDebug(" unregistering component '" + classDesc.className + "'");
  112.         // callback to allow classDesc specific unregistration
  113.         classDesc.unregister(aCompMgr, aFileSpec, aLocation, aType);
  114.         FlockXPCOMUtils.deleteCategories(classDesc);
  115.         registrar.unregisterFactoryLocation(classDesc.classID, aFileSpec);
  116.       }
  117.       this.printDebug("leaving unregisterSelf");
  118.     };
  119.  
  120.     this.getClassObject = function genericModule_getClassObject(aCompMgr, aCID, iid) {
  121.       this.printDebug("getClassObject() called");
  122.  
  123.       // XXX: Right now, we only support nsIFactory queries, not nsIClassInfo
  124.       // However, we do provide an nsIClassInfo for components that use us
  125.       // so perhaps we *could*. But I'm unsure about the semantics, and
  126.       // there appears to be no users of that feature in the tree. -Manish
  127.       if (!iid.equals(CI.nsIFactory)) {
  128.         this.printDebug("(error) iid was '" + iid + "'");
  129.         throw CR.NS_ERROR_NOT_IMPLEMENTED;
  130.       }
  131.  
  132.       for each (let classDesc in this.classes) {
  133.         if (aCID.equals(classDesc.classID)) {
  134.           this.printDebug(" ClassID MATCHES component '" + classDesc.className + "'");
  135.           return classDesc.factory;
  136.         }
  137.         this.printDebug(" ClassID does not match component '" + classDesc.className + "'");
  138.       }
  139.  
  140.       // no component had a matching contract id
  141.       this.printDebug("(ERROR) unable to match cid='" + aCID + "'");
  142.       throw CR.NS_ERROR_FACTORY_NOT_REGISTERED;
  143.     };
  144.  
  145.     this.QueryInterface = function genericModule_QueryInterface(iid) {
  146.       if (iid.equals(CI.nsIModule) ||
  147.           iid.equals(CI.nsISupports))
  148.       {
  149.         return this;
  150.       }
  151.       throw CR.NS_ERROR_NO_INTERFACE;
  152.     };
  153.  
  154.     this.canUnload = function genericModule_canUnload(aCompMgr) {
  155.       this.printDebug("canUnload called");
  156.       return true; // return value is ignored for javascript components
  157.     };
  158.   },
  159.  
  160. /**
  161.  * Creates a generic Javascript XPCOM component with a set of predefined
  162.  * interfaces: nsISupports, nsISupportsPrimitive, nsISupportsCString, nsIClassInfo.
  163.  *
  164.  * @param aName (in)       A descriptive name for the component. Becomes the
  165.  *                         nsIClassInfo property "classDescription"
  166.  * @param aCID (in)        The ClassID for this component.
  167.  * @param aContractID (in) The ContractID for this component.
  168.  * @param aCtor (in)       The constuctor for the object. Usage: return new aCtor();
  169.  * @param aClassFlags (in) Class from nsIClassInfo flags ORed together. To make
  170.  *                         your component a singleton,
  171.  *                         use Components.interfaces.nsIClassInfo.SINGLETON.
  172.  * @param aInterfaceArray (in) The list of interfaces the component supports.
  173.  *                         Do not list any of the predefined interfaces here.
  174.  *                         Example:
  175.  *                         [Components.interfaces.flockILoggingService, Components.interfaces.nsIObserver]
  176.  * @return An object that implements a class factory for the listed interfaces, and
  177.  *         can be QueryInterfaced()'ed for any of the supported interfaces.
  178.  *
  179.  * @see nsIClassInfo
  180.  */
  181.   genericComponent: function FlockXPCOMUtils_genericComponent(aName, aCID, aContractID, aCtor, aClassFlags, aInterfaceArray) {
  182.     // We need to use "this" as it was during component creation, so we
  183.     // take a copy now. Within genericComponent(), references to "this"
  184.     // are from the context of the caller of the defined function.
  185.     var inst = this;
  186.  
  187.     inst.ctor = aCtor;              // For creating new instances
  188.     inst.classDescription = aName;  // Property of nsIClassInfo interface
  189.  
  190.     // use FlockXPCOMUtils as the default for printing debugging statements
  191.     inst.showdebug = FlockXPCOMUtils.debug;
  192.  
  193.     // override printDebug() to make messages slightly more informative
  194.     inst.printDebug = function genericComponent_printDebug(message) {
  195.       if (inst.showdebug) {
  196.         FlockXPCOMUtils.printDebug("FlockXPCOMUtils Cmp: " +inst.classDescription+ ": " + message);
  197.       }
  198.     };
  199.  
  200.     // Build the list of interfaces supported by the component.
  201.     inst.interfaceArray = [];
  202.  
  203.     // All components have nsISupports, so add it automatically.
  204.     inst.interfaceArray.push(Components.interfaces.nsISupports);
  205.  
  206.     // We provide nsIClassInfo for components using this module.
  207.     inst.interfaceArray.push(Components.interfaces.nsIClassInfo);
  208.  
  209.     inst.predefinedInterfaceCount = inst.interfaceArray.length;
  210.  
  211.     // Record the interfaces supported by the component.
  212.     for (var i = 0; i < aInterfaceArray.length; i++) {
  213.       inst.interfaceArray.push(aInterfaceArray[i]);
  214.     }
  215.  
  216.     // Check that the caller actually gave an interface to use
  217.     if (inst.interfaceArray.length === inst.predefinedInterfaceCount) {
  218.       inst.printDebug("(WARNING) no user specified interfaces");
  219.     }
  220.  
  221.     // warn if it is obvious something else has been passed instead of a CID
  222.     //   (the raw xpconnect error is obscure enough to warrant a warning)
  223.     if (typeof(aCID) !== "object") {
  224.       inst.printDebug("(WARNING) cid '" + aCID + "' is not an object!");
  225.     }
  226.  
  227.     // register() is a callback from genericModule.registerSelf()
  228.     inst.register = function genericComponent_register(aCompMgr, aFileSpec, aLocation, aType) {
  229.       inst.printDebug("register called on component '" + this.classDescription + "'");
  230.       return;  // user overrides 'register()' for component specific registration
  231.     };
  232.  
  233.     // unregister() is a callback from genericModule.unregisterSelf()
  234.     inst.unregister = function genericComponent_unregister(aCompMgr, aFileSpec, aLocation, aType) {
  235.       inst.printDebug("unregister called on component '" + this.classDescription + "'");
  236.       return;  // user can override for component specific unregistration
  237.     };
  238.  
  239.     // Convenience wrapper around FlockXPCOMUtils.addCategories
  240.     inst.addCategories = function genericComponent_addCategories() {
  241.       inst.printDebug("instance manually adding categories");
  242.       var classDesc = FlockXPCOMUtils.classDescForComponent(inst.ctor);
  243.       FlockXPCOMUtils.addCategories(classDesc);
  244.     };
  245.  
  246.     // Convenience wrapper around FlockXPCOMUtils.deleteCategories
  247.     inst.deleteCategories = function genericComponent_deleteCategories() {
  248.       inst.printDebug("instance manually deleting categories");
  249.       var classDesc = FlockXPCOMUtils.classDescForComponent(inst.ctor);
  250.       FlockXPCOMUtils.deleteCategories(classDesc);
  251.     };
  252.  
  253.     // nsISupports interface
  254.     inst.QueryInterface = function genericComponent_QueryInterface(iid) {
  255.       inst.printDebug("QueryInterface called");
  256.  
  257.       // see if component object supports the requested interface
  258.       for (i = 0; i < inst.interfaceArray.length; i++) {
  259.         if (iid.equals(inst.interfaceArray[i])) {
  260.           inst.printDebug(" MATCHED interface '" + inst.interfaceArray[i] + "'");
  261.           return this;
  262.         }
  263.       }
  264.  
  265.       if (inst.showdebug) { // expensive but rare check to get better debug message
  266.         for (var name in Components.interfaces) {
  267.           if (iid.equals(Components.interfaces[name])) {
  268.             inst.printDebug(" NS_ERROR_NO_INTERFACE on QI for '" + name + "'");
  269.             throw Components.results.NS_ERROR_NO_INTERFACE;
  270.           }
  271.         }
  272.         inst.printDebug(" NS_ERROR_NO_INTERFACE on QI for '" + iid + "'");
  273.       }
  274.       throw Components.results.NS_ERROR_NO_INTERFACE;
  275.     };
  276.  
  277.     // nsIClassInfo interface
  278.     inst.getInterfaces = function genericComponent_getInterfaces(aCount) {
  279.       var iids = [];
  280.       for (i = 0; i < inst.interfaceArray.length; i++) {
  281.         iids.push(inst.interfaceArray[i]);
  282.       }
  283.       aCount.value = iids.length;
  284.       return iids;
  285.     };
  286.     inst.getHelperForLanguage = function genericComponent_getHelperForLanguage(aLanguage) { return null; };
  287.     inst.contractID = aContractID;
  288.     inst.classID = aCID;
  289.     inst.implementationLanguage = Components.interfaces.nsIProgrammingLanguage.JAVASCRIPT;
  290.     // See xpcom/components/nsIClassInfo.idl for more flags.
  291.     inst.flags = aClassFlags;
  292.  
  293.     // required by nsIFactory so define to prevent warnings
  294.     inst.lockFactory = function genericComponent_lockFactory(lock) {
  295.       inst.printDebug("lockFactory() called");
  296.       return true;
  297.     };
  298.  
  299.     // nsIFactory function pointed to by module.getClassObject()
  300.     inst.createInstance = function genericComponent_createInstance(outer, iid) {
  301.       inst.printDebug("createInstance called for " + inst.classDescription);
  302.  
  303.       if (outer) {
  304.         /* FIXME: tell me again why don't we handle aggregation? */
  305.         inst.printDebug("(ERROR) interface does not allow aggregation");
  306.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  307.       }
  308.       inst.printDebug(" creating new instance and querying for interface");
  309.       return (new inst.ctor()).QueryInterface(iid);
  310.     };
  311.   },
  312.  
  313.   // helper function to add an entry to the category manager
  314.   addCategoryEntry: function FlockXPCOMUtils_addCategoryEntry(category, name, value) {
  315.     this.printDebug("FlockXPCOMUtils addCategory: adding '"
  316.                     + name
  317.                     + "' to category '"
  318.                     + category
  319.                     + "'");
  320.     /* NOTE: different categories use different entries for name and value */
  321.     this.categoryManager.addCategoryEntry(category, name, value, true, true);
  322.   },
  323.  
  324.   // helper function to remove an entry from the category manager
  325.   deleteCategoryEntry: function FlockXPCOMUtils_deleteCategoryEntry(category, name) {
  326.     this.printDebug("FlockXPCOMUtils deleteCategory: deleting '"
  327.                     + name
  328.                     + "' from category '"
  329.                     + category
  330.                     + "'");
  331.     this.categoryManager.deleteCategoryEntry(category, name, true);
  332.   },
  333.  
  334.   // helper function add categories to the category manager out of
  335.   // component.prototype._xpcom_categories
  336.   addCategories: function FlockXPCOMUtils_addCategories(classDesc) {
  337.     if (classDesc.categories) {
  338.       for each (let cat in classDesc.categories) {
  339.         let defaultValue = (cat.service ? "service," : "") +
  340.                            classDesc.contractID;
  341.         this.addCategoryEntry(cat.category,
  342.                               cat.entry || classDesc.className,
  343.                               cat.value || defaultValue);
  344.       }
  345.     }
  346.   },
  347.  
  348.   // helper function delete categories to the category manager out of
  349.   // component.prototype._xpcom_categories
  350.   deleteCategories: function FlockXPCOMUtils_deleteCategories(classDesc) {
  351.     if (classDesc.categories) {
  352.       for each (let cat in classDesc.categories) {
  353.         this.deleteCategoryEntry(cat.category,
  354.                                  cat.entry || classDesc.className);
  355.       }
  356.     }
  357.   },
  358.  
  359.   classDescForComponent: function FlockXPCOMUtils_classDescForComponent(aComponent) {
  360.     return {
  361.       classID: aComponent.prototype.classID,
  362.       className: aComponent.prototype.classDescription,
  363.       contractID: aComponent.prototype.contractID,
  364.       factory: aComponent.prototype,  // component acts as its own nsIFactory
  365.       categories: aComponent.prototype._xpcom_categories,
  366.       register: aComponent.prototype.register,
  367.       unregister: aComponent.prototype.unregister
  368.     };
  369.   },
  370.  
  371.   get categoryManager() {
  372.     return CC["@mozilla.org/categorymanager;1"]
  373.            .getService(CI.nsICategoryManager);
  374.   },
  375.  
  376.   // print a debug message (turn on and off via FlockXPCOMUtils.debug)
  377.   printDebug: function FlockXPCOMUtils_printDebug(message) {
  378.     if (this.debug) dump(message + "\n");
  379.   }
  380. }; // var FlockXPCOMUtils
  381.